use crate::staker::StakeApy;
use cosmwasm_std::{Api, Extern, Querier, StdError, StdResult, Storage, Uint128};
use cosmwasm_storage::{Bucket, ReadonlyBucket, ReadonlySingleton, Singleton};
use hermit_toolkit_hmip21::asset::AssetInfo;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

static POOLS_KEY: &[u8] = b"pools";
static POOLS_MAP: &[u8] = b"pool_map";

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct PoolInfo {
    pub pool_id: u8,
    pub reward_asset: AssetInfo,
    pub stake_asset: AssetInfo,
    pub reward_amount: Uint128,
    pub stake_amount: Uint128,
    pub flexible_stake_amount: Uint128,
    pub withdraw_lock_period: u64,
    pub average_lock_duration: u64,
    pub withdraw_fee: u64,
    pub stake_apy: Vec<StakeApy>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct PoolsMap {
    pub pool_id: u8,
}
pub fn store_pools_map<S: Storage>(storage: &mut S, data: &Vec<PoolsMap>) -> StdResult<()> {
    Singleton::new(storage, POOLS_MAP).save(data)
}
pub fn read_pools_map<S: Storage>(storage: &S) -> StdResult<Vec<PoolsMap>> {
    ReadonlySingleton::new(storage, POOLS_MAP).load()
}

pub fn store_pool<S: Storage>(storage: &mut S, data: &PoolInfo) -> StdResult<()> {
    let binding = data.pool_id.to_string();
    let key = &binding.as_bytes();
    let mut pool_bucket: Bucket<S, PoolInfo> = Bucket::new(POOLS_KEY, storage);
    pool_bucket.save(key, &data)?;
    
    let mut pools_map: Vec<PoolsMap> = vec![];
    let pools_map_f = read_pools_map(storage);
    if pools_map_f.is_ok() {
        pools_map = pools_map_f.unwrap();
    }
    pools_map.push(PoolsMap {
        pool_id: data.pool_id,
    });
    store_pools_map(storage, &pools_map)?;

    Ok(())
}

pub fn read_pool_by_pool_id<S: Storage>(storage: &S, pool_id: u8) -> StdResult<PoolInfo> {
    let pool_bucket: ReadonlyBucket<S, PoolInfo> = ReadonlyBucket::new(POOLS_KEY, storage);
    match pool_bucket.load(pool_id.to_string().as_bytes()) {
        Ok(v) => Ok(v),
        Err(_e) => Err(StdError::generic_err("no pool data stored")),
    }
}

pub fn read_pools<S: Storage, A: Api, Q: Querier>(
    deps: &Extern<S, A, Q>,
) -> StdResult<Vec<PoolInfo>> {
    let pools_map = read_pools_map(&deps.storage)?;
    let mut iter = pools_map.iter();
    let mut pools = vec![];
    for _ in iter.clone() {
        let pool_map = iter.next();
        if pool_map.is_none() {
            break;
        }
        pools.push(read_pool_by_pool_id(
            &deps.storage,
            pool_map.unwrap().pool_id,
        )?)
    }

    Ok(pools)
}
